/**
 * Author(s): xuming.Wong <xuming.Wong@gmail.com>
 */
#include "SceneObject.h"
#include "GameObject.h"
#include "CameraObject.h"

namespace PQEngine
{
	SceneRenderProperty::SceneRenderProperty(SceneObject* obj)
		: _backgroundColor(0.0f, 0.0f, 0.0f),_ambient(0.5f,0.5f,0.5f),_fogColor(),
		_fogDensity(0.0f),_mode(Ogre::PM_SOLID),_sceneObject(obj)
	{
		assert(_sceneObject);
	}

	void SceneRenderProperty::setBackground(Ogre::ColourValue color)
	{
		_backgroundColor=color;
		_sceneObject->setBackground(_backgroundColor);
	}

	void SceneRenderProperty::setAmbient(Ogre::ColourValue color)
	{
		_ambient=color;
		_sceneObject->setAmbient(_backgroundColor);
	}

	void SceneRenderProperty::setPolyMode(Ogre::PolygonMode mode)
	{
		_mode=mode;
		_sceneObject->setPolyMode(_mode);
		
	}

	SceneObject *SceneObject::_activeScene=0;

	SceneObject::SceneObject(Ogre::RenderWindow* window,Ogre::SceneType sceneType) : Object(OBJECT_TYPE_SCENE_OBJECT)
		,_rootObject(0),_cameraObject(0),_isActivate(false),_inited(false),_window(window),_selectMeshData(0),
		_selectedObject(0),_sceneManager(0),_renderProperty(0),_mouseX(0),_mouseY(0),_sceneType(sceneType)
	{
		if(!Ogre::Root::getSingletonPtr()){
			//OGRE Root must created
			OGRE_EXCEPT(Ogre::Exception::ERR_INTERNAL_ERROR,
					"failed to create object",
					"SceneObject::create");
		}
		_renderProperty=new SceneRenderProperty(this);
		_selectMeshData=new MeshData();
		if(_activeScene){
			_activeScene->setIsActivate(false);
		}
		this->setIsActivate(true);
		_activeScene=this;
		
		loadScene(_sceneType);
	}

	Ogre::RenderWindow* SceneObject::getRenderWindow()
	{
		return _window;
	}

	GameObject* SceneObject::getRoot()
	{
		return _rootObject;
	}

	void SceneObject::setBackground(Ogre::ColourValue color)
	{
		if(_cameraObject){
			_cameraObject->setBackgroundColour(color);
		}
	}

	SceneObject::~SceneObject()
	{
		if(_renderProperty){
			delete _renderProperty;
		}
	}

	void SceneObject::activate()
	{
		if(!_inited){
			loadScene(_sceneType);
			_inited=true;
		}

		//reset the viewport that render scene
		if(_cameraObject){
			_cameraObject->resetViewport();
		}


		if(_activeScene){
			_activeScene->setIsActivate(false);
		}
		this->setIsActivate(true);
		_activeScene=this;
		
	}

	void SceneObject::loadScene(Ogre::SceneType sceneType)
	{
		if(_inited){
			return;
		}

		Ogre::String name="SceneManager";
		name+=Ogre::StringConverter::toString(getId());

		_sceneManager = Ogre::Root::getSingleton().createSceneManager(sceneType,name);
		
		//create root game object
		_rootObject=new GameObject(this,GAME_OBJECT_ROOT);

		_cameraObject=new CameraObject(this);
		_cameraObject->setFarClipDistance(10000);
		_cameraObject->setNearClipDistance(1);

		
		_sceneManager->setAmbientLight(_renderProperty->ambient());//render settings

		// Create RaySceneQuery for select object
		_raySceneQuery = _sceneManager->createRayQuery(Ogre::Ray());
		_raySceneQuery->setQueryTypeMask(Ogre::SceneManager::ENTITY_TYPE_MASK);
		_raySceneQuery->setSortByDistance(true);
		
		_inited=true;
	}


	Ogre::Ray SceneObject::getRay(int x,int y)
	{
		return _cameraObject->getRay(x,y);
	}

	void SceneObject::setPolyMode(Ogre::PolygonMode mode)
	{
		if(_cameraObject){
			_cameraObject->setPolygonMode(mode);
		}
	}

	void SceneObject::update(float deltaT)
	{
		if(_rootObject){
			_rootObject->update(deltaT);
		}
	}

	

	Ogre::SceneNode* SceneObject::createChildSceneNode()
	{
		Ogre::SceneNode* node=_sceneManager->getRootSceneNode()->createChildSceneNode();
		return node;
	}

	MeshObject* SceneObject::loadMeshObject(std::string meshName)
	{
		MeshObject* obj=new MeshObject(this,meshName);

		return obj;
	}

	GameObject* SceneObject::selectObject(int id)
	{
		if(_selectedObject){
			_selectedObject->selectObject(false);		
		}
		_selectedObject=0;

		Object* obj=Object::getObject(id);
		if(Object::isGameObject(obj)){
			_selectedObject=(GameObject*)obj;
			_selectedObject->selectObject(true);
			if(!_selectedObject->isTerrain()){
				//_terrainManager->deselectTerrain();
			}
			return _selectedObject;
		}
		
		return _selectedObject;
	}

	GameObject* SceneObject::selectObject(int x,int y)
	{
		if(_selectedObject){
			_selectedObject->selectObject(false);//becarefull select and select object
			if(_selectedObject->isTerrain()){
				//_terrainManager->deselectTerrain();
			}
		}

		Ogre::Ray ray = getRay(x,y);
		//_selectedObject= _terrainManager->selectTerrain(x,y);
		GameObject* obj= getObject(ray);
		if(obj){//if have object selected.
			//override terrain object by normal game object.
			//because the normal game object is prefer than terrain
			_selectedObject=obj;
			//_terrainManager->deselectTerrain();
		}

		if(_selectedObject){
			_selectedObject->selectObject(true);
		}

		return _selectedObject;
	}

	GameObject* SceneObject::getObject(Ogre::Ray ray)
	{
		GameObject* obj=0;
        _raySceneQuery->setRay(ray);
		//OGRE sort query by distance already
		_raySceneQuery->setSortByDistance(true);
        // Execute query
		Ogre::RaySceneQueryResult &result = _raySceneQuery->execute();
        Ogre::RaySceneQueryResult::iterator itr = result.begin();
		
		 // loop through the results
		for(; itr!=result.end(); ++itr){
			 if (itr->movable){
				 Ogre::SceneNode* node= itr->movable->getParentSceneNode();
				 if(itr->movable->getMovableType().compare("Entity")==0){
					Ogre::Entity* entity=static_cast<Ogre::Entity*>(itr->movable);

					_selectMeshData->loadMeshEntity(entity,node);
					if(_selectMeshData->intersect(ray)){
						obj=GameObject::get(node);
						if(obj){//find and return
							  return obj;
						}
					}
				}
			 }
		 }
		
		return obj;
	}


	GameObject* SceneObject::getGameObject()
	{
		return _selectedObject;
	}


	void SceneObject::resetScreenResolution(int width,int height)
	{
		if(_window){
			_window->windowMovedOrResized();
		}
		if(_cameraObject){
			_cameraObject->setAspectRatio(width,height);
		}
	}

	GameObject* SceneObject::getObject(int x,int y)
	{
		//Just get object not select it
		Ogre::Ray ray =getRay(x,y);
		GameObject* obj= getObject(ray);
		if(!obj){
			//obj=_terrainManager->getTerrain(x,y);
			return obj;
		}
		return obj;
	}

	void SceneObject::enableSkyDome(bool enabled)
	{
		//
	}

	void SceneObject::OnMouseMove(int x,int y)
	{
		//if(_terrainManager){
			//_terrainManager->OnMouseMove(x,y);
		//}

		_mouseX=x;
		_mouseY=y;
	}


	Ogre::Entity* SceneObject::createEntity(const Ogre::String& entityName, const Ogre::String& meshName)
	{
		if(_sceneManager){
			return _sceneManager->createEntity(entityName,meshName);
		}
		return 0;
	}

	Ogre::Entity* SceneObject::createEntity(const Ogre::String& meshName)
	{
		if(_sceneManager){
			return _sceneManager->createEntity(meshName);
		}
		return 0;
	}

}
